import weka.core.converters.ConverterUtils.DataSource;
import weka.core.Instances;
import weka.core.Instance;
import weka.classifiers.Evaluation;
import weka.classifiers.Classifier;

import java.io.File;
import java.io.PrintWriter;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.util.Scanner;

import java.lang.Class;
import java.lang.reflect.Method;

public class Classify {
    // For testing
    public static void main(String[] args) throws Exception {
        double[] features = new double[81];
        Scanner sc = new Scanner(new File("java/test_input.txt"));
        sc.useDelimiter(",");
        for(int i = 0; i < 81; ++i) {
            features[i] = Double.parseDouble(sc.next());
        }
        Classify solver = new Classify();
        System.out.println(solver.classify(features));
    }

    // Classify a video
    // @params:
    //    features: the generated features
    public boolean classify(double[] features) throws Exception {
        // First step: Reformat the features to WEKA Instances
        // We write the features to an ARFF file using a prepared template
        Scanner sc = new Scanner(new File("java/video_template.arff"));
        sc.useDelimiter("XXXXX");
        String s = sc.next();

        PrintWriter pw = new PrintWriter(new File("java/video.arff"));
        pw.println(s);
        for(int i = 0; i < features.length; ++i)
            pw.print(features[i] + ",");
        pw.println("?");
        pw.close();

        // Load the Instance from created ARFF file
        Instances data = loadData("java/video.arff");

        // Restore the classifier, which is trained in previous step
        Classifier classifier = (Classifier) deserializeClassifier("java/mlp.model");

        // Use the classifier for classification
        data.instance(0).setClassValue(-1.0);
        double guess = 0.0;
        try {
            guess = classifier.classifyInstance(data.instance(0));
        }
        catch (Exception ex) {
            System.out.println("Error predicting: " + ex.getMessage());
        }

        if ((int) guess == 0) return true;
        else return false;
    }

    /*
     * Deserialize a Classifier and return it
     * filename   = Classifier filename
     */
    private Object deserializeClassifier(String filename) {
        Object classifier = null;
        try {
            ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream(new File(filename))
            );
            classifier = ois.readObject();
            ois.close();
        }
        catch (Exception ex) {
            System.out.println("Error restoring classifier model: " + ex.getMessage());
            ex.printStackTrace();
        }
        return classifier;
    }

    /*
     * Loads classification instances from file.
     * Auto detect class / label
     * args = input file name
     */
    private Instances loadData(String args) {
        return loadData(args, -1);
    }
    private Instances loadData(String args, int labelIndex) {
        Instances data = null;
        try {
            DataSource source = new DataSource(args);
            data = source.getDataSet();

            if (labelIndex < 0) { // No label specified. Try to auto detect
                boolean foundLabel = false;
                for(int i = 0; i < data.numAttributes(); ++i)
                    if (data.attribute(i).name().equals("label")
                            && data.attribute(i).isNominal()) {
                        data.setClassIndex(i);
                        foundLabel = true;
                        break;
                    }
                if (!foundLabel) {
                    if (data.attribute(data.numAttributes()-1).isNominal()) {
                        data.setClassIndex(data.numAttributes() - 1);
                    }
                    else {
                        throw new Exception("No class attribute detected");
                    }
                }
            }
            else {
                if (data.attribute(labelIndex).isNominal() == false) {
                    throw new Exception("Invalid class attribute");
                }
            }
        }
        catch (Exception ex) {
            System.out.println("Error reading file " + args + ": " + ex.getMessage());
        }
        return data;
    }
}
// Build: javac -cp java/weka.jar Classify.java
// Classify:    java -Xmx2108m -cp java/weka.jar:. Classify
